rollout Common_SelectFaceByUVWTool "依据UnwrapUVW选面"
(
    button btnOpenPanel "打开依据UVW选面工具" toolTip:"打开依据UVW选面工具" align:#center
	on btnOpenPanel pressed do
	(
        if Common_SelectFaceByUVW != undefined then
        (
            DestroyDialog Common_SelectFaceByUVW
        )
        createDialog Common_SelectFaceByUVW width:220
	)
)

rollout Common_VertexPaintToSelectedFaceTool "给选中面刷顶点色"
(
    button btnOpenPanel "打开给选中面刷顶点色工具" toolTip:"打开给选中面刷顶点色工具" align:#center
	on btnOpenPanel pressed do
	(
        if Common_VertexPaintToSelectedFace != undefined then
        (
            DestroyDialog Common_VertexPaintToSelectedFace
        )
        createDialog Common_VertexPaintToSelectedFace width:220
	)
)

rollout Common_NormalMappingTool "Normal映射"
(
    button btnOpenPanel "打开Normal映射工具" toolTip:"打开Normal映射工具" align:#center
	on btnOpenPanel pressed do
	(
        if Common_NoorsNormalThief != undefined then
        (
            DestroyDialog Common_NoorsNormalThief
        )
        createDialog Common_NoorsNormalThief width:220
	)
)

rollout Common_RolloutSolveExportGimbalLock "FBX GimbalLock修复"
(
	/*
	逻辑：
	填入报错骨骼
	填入相关mesh

	对报错骨骼 assume skin pose
	-- 根据报错骨骼找mesh

	for mesh
		mesh 缩放为 100
		extract skin data to mesh  新产生的mesh为 SkinData_原mesh名
		记录skin modifier上的boneList
		对skin modifier 做 collapse to
		新加skin modifier，加boneList
		传权重
		删掉 SkinData_原mesh名
	*/


	-- string1 = "-zhuozi"
	-- string2 = replace string1 1 1 "$"

	-- namesArray = for s in selection collect s.name

	fn getSkinBones obj =
	(
		max modify mode
		select obj
		
		if obj.modifiers[#Skin] != undefined do
		(
			skinBones = #()
			skinModifiers = getclassinstances Skin target:obj
			
			for s = 1 to skinModifiers.count where skinModifiers.count > 0 do
			(
				boneCount = skinOps.getnumberbones skinModifiers[s]
				for x = 1 to boneCount do
				(
					myBone = (skinOps.getBoneName skinModifiers[s] x 0)
					append skinBones (getNodeByName myBone)
				)
			)
			
			print skinBones
		)
		return skinBones
	)


	-- Not considering multi skin modifiers condition
	fn reSkins objName =
	(
		obj = getNodeByName objName
		print obj
		obj.scale = [1,1,1]
		
		-- refresh mesh size
		sliderTime = 1f
		sliderTime = 0f 
		
		skinUtils.ExtractSkinData obj
		
		max modify mode
		select obj
		
		if obj.modifiers[#Skin] != undefined do
		(
			skinBones = #()
			skinModifiers = getclassinstances Skin target:obj
			
			if skinModifiers.count > 1 then
			(	
				print skinModifiers.count
				print "Warning! Too many Skin modifiers!"
				return 0
			)
			else
			(	
				if skinModifiers.count == 1 do
				(
					boneCount = skinOps.getnumberbones skinModifiers[1]
					for i = 1 to boneCount do
					(
						myBone = (skinOps.getBoneName skinModifiers[1] i 0)
						append skinBones (getNodeByName myBone)
					)				
				)

				print skinBones

			)
			/*
			for modifierIndex = 1 to obj.modifiers.count do
			(	
				if obj.modifiers[modifierIndex] == skinModifiers[1] then 
				(
					skinModifiers[1].mirrorEnabled = off
					-- deleteModifier obj skinModifiers[1]
					maxOps.CollapseNodeTo obj modifierIndex off
				)
			)
			*/
			-- modPanel.setCurrentObject skinModifiers[1]
			modifierIndex = modPanel.getModifierIndex obj skinModifiers[1]
			maxOps.CollapseNodeTo obj modifierIndex off

			addModifier obj (Skin())
			newSkin = obj.modifiers[#Skin]
			
			skinDataObjName = "SkinData_" + objName
			skinDataObj = getNodeByName skinDataObjName
			
			for skinBoneIndex=1 to skinBones.count do
			(	
				skinBone = skinBones[skinBoneIndex]
				modPanel.setCurrentObject newSkin
				skinOps.addBone newSkin skinBone skinBoneIndex
				print skinBone
			)

			select #(obj, skinDataObj)
			skinUtils.ImportSkinDataNoDialog true false false false false 1.0 0
			-- newSkin.always_deform = false
			-- newSkin.enabled=true
			-- newSkin.always_deform = true
			delete skinDataObj
			clearSelection()
		)
	)

	fn batching boneArray meshArray=
	(
		-- boneArray = #("huatong", "shanzi", "muban", "Bone001", "Bone003", "Bone005", "zhuozi")
		-- meshArray = #("SM_af32mrr_csdesk_01", "SK_af32mrr_default_CG")

		sliderTime = 0f

		for boneName in boneArray do
		(
			myBone = getNodeByName boneName
			myBone.assumeSkinPose()
			skinModifiers = getclassinstances Skin target:myBone
			for index = 1 to skinModifiers.count do deleteModifier myBone skinModifiers[index]
		)

		for meshName in meshArray do
		(
			reSkins meshName
		)
	)

	group ""
	(
		edittext boneList "骨骼/控制器:" height:100 labelOnTop:true
		listbox meshList "模型:" height:7 items:#() readOnly:false
		
		button addMeshButton "添加模型" toolTip:"将选择的模型添加到上面的列表中" align:#center across:2 offset:[0,2]
		button deleteMeshButton "删除模型" toolTip:"删除上面的列表中选择的模型" align:#center offset:[0,2]
	)
	
	button batchingButton "开始批处理" toolTip:"1.让骨骼/控制器回到初始Pose（Assume Skin Pose）\n2.模型还原缩放到100\n3.重做Skin，还原权重"
	
	on addMeshButton pressed do
	(	
		tempArray = meshList.items
		for obj in selection do
		(	
			objName = obj.name
			if (findItem tempArray objName) != 0 or (classOf obj as string) != "PolyMeshObject" do continue
			meshList.items = append meshList.items objName
		)
	)

	on deleteMeshButton pressed do
	(
		if meshList.items.count > 0 and meshList.selection > 0 do
		(
			meshList.items = deleteItem meshList.items meshList.selection
		)
	)

	on batchingButton pressed do
	(
		boneArray = FilterString boneList.text "- \r\n"
		meshArray = meshList.items
		batching boneArray meshArray
	)
)

rollout Common_AssembleTool "动画组装工具"
(	
	button btnOpenDialogPanel "动画组装工具" toolTip:"打开动画组装工具面板" align:#center width:100
	on btnOpenDialogPanel pressed do
	(
		if assembleAnimDialog != undefined then
		(
			DestroyDialog assembleAnimDialog
		)
		createDialog assembleAnimDialog modal:false
	)
)

-- FBX可以用FBX - Merge操作合并回Max
rollout MocapBipedToolsRollout "动捕相关工具" width:250 height:244
(
    group "添加Root/*Origin点"
    (
        label lblAddRoot "请选中骨架的质心点(如:Bip01)" align:#left width:190 across:2
        button btnAddRoot "添加" align:#right width:40
    )
    group "生成TPose Fbx"
    (
        editText textFBXSuffix "Fbx文件后缀" width:160 text:"_TPose" align:#left
        checkbox chkBipedOnly "只输出CS骨骼" default:false align:#Left
        label lblTPoseExport "输出Tpose Fbx到Max文件目录" width:180 align:#left across:2
        button btnTPoseExport "输出" width:40 align:#right
    )
    group "动捕数据应用到Max文件"
    (
        label lbl1 "1  打开需要应用动捕fbx的Max文件" align:#left
        label lbl2 "2" across:3 align:#left width:10 offset:[0,8]
        edittext MocapFbxPath "" width:160 offset:[-65,6] text:"请选择动捕fbx源文件目录"
        button btnSelectMocapFbxPath "选择" width:40 offset:[0,6] height:18 align:#right
        label lbl3 "3" across:2 align:#left width:10 offset:[0,6]
        button btnatchExecute "批处理转换" offset:[-100,0] width:100 align:#left toolTip:"根据所有动捕动作fbx，依次生成对应max文件（帧速率60FPS）"
    )
	
    fn TraversalChildNode parentNode selectionArray = 
    (
        if parentNode == undefined then
        (
            return false
        )

        for childnode in parentNode.children do
        (
            if childnode.children.count > 0 then
            (
                if not (TraversalChildNode childnode selectionArray) then
                    return false
            )
            else
            (
                append selectionArray childnode
            )
        )
        append selectionArray parentNode
        return true
    )

    fn SelectSkeletonAll = 
    (
        local selectionArray = #()
        local ret = TraversalChildNode $Root selectionArray
        if ret then
        (
            selectMore selectionArray
        )
        return ret
    )

    fn GetAllBipedObjects = 
    (
        local bipedObjects = #()
        for geo in geometry where (isKindOf geo Biped_Object) do
        (
	        append bipedObjects geo
        )
        return bipedObjects
    )

	fn getFilesRecursive root pattern =
	(
		local dir_array = GetDirectories (root+"/*")
		for d in dir_array do
			join dir_array (GetDirectories (d+"/*"))
		local my_files = getFiles (root+"/*")
		for f in dir_array do
			join my_files (getFiles (f + pattern))
		return my_files
	)
	
    on btnAddRoot pressed do
    (
        if $ != undefined then
        (
            if getnodebyname "Root" == undefined then
                Dummy pos:[0,0,0] name:"Root" boxSize:[20,20,20]
            if getnodebyname "*origin" == undefined then
                Dummy pos:[0,0,0] name:"*origin" boxSize:[10,10,10]

            $.parent = $Root
            messageBox("添加Root和*Origin点成功!    ")
        )
        else
        (
            messageBox("请选中骨架的质心点!    ")
        )
    )

    on btnTPoseExport pressed do
    (
        setquietmode true
        max hold

        max unhide all #noPrompt
        local filename = maxfilepath + maxfilename
        if filename == "" then
        (
            setquietmode false
            messagebox("操作失败 Max文件异常!          ")
            return false
        )

        fbxExporterSetParam "Animation" false
        sliderTime = 0

        local rootBone = $Root
        if undefined == rootBone then
        (
            setquietmode false
            messagebox("操作失败 骨架上没有Root点!          ")
            return false
        )

        local BipedBones = GetAllBipedObjects()

        local L_Clavicle = undefined
        local L_UpperArm = undefined
        local L_Forearm = undefined
        local L_Hand = undefined

        local R_Clavicle = undefined
        local R_UpperArm = undefined
        local R_Forearm = undefined
        local R_Hand = undefined

        local L_Finger0Arr = #()
        local L_Finger1Arr = #()
        local L_Finger2Arr = #()
        local L_Finger3Arr = #()
        local L_Finger4Arr = #()

        local R_Finger0Arr = #()
        local R_Finger1Arr = #()
        local R_Finger2Arr = #()
        local R_Finger3Arr = #()
        local R_Finger4Arr = #()

        for BoneNode in BipedBones do
        (
            if (findString BoneNode.name "L Clavicle" != undefined) then
                L_Clavicle = BoneNode
            if (findString BoneNode.name "L UpperArm" != undefined) then
                L_UpperArm = BoneNode
            if (findString BoneNode.name "L Forearm" != undefined) then
                L_Forearm = BoneNode
            if (findString BoneNode.name "L Hand" != undefined) then
                L_Hand = BoneNode
            if (findString BoneNode.name "L Finger0" != undefined) then
                append L_Finger0Arr BoneNode
            if (findString BoneNode.name "L Finger1" != undefined) then
                append L_Finger1Arr BoneNode
            if (findString BoneNode.name "L Finger2" != undefined) then
                append L_Finger2Arr BoneNode
            if (findString BoneNode.name "L Finger3" != undefined) then
                append L_Finger3Arr BoneNode
            if (findString BoneNode.name "L Finger4" != undefined) then
                append L_Finger4Arr BoneNode

            if (findString BoneNode.name "R Clavicle" != undefined) then
                R_Clavicle = BoneNode
            if (findString BoneNode.name "R UpperArm" != undefined) then
                R_UpperArm = BoneNode
            if (findString BoneNode.name "R Forearm" != undefined) then
                R_Forearm = BoneNode
            if (findString BoneNode.name "R Hand" != undefined) then
                R_Hand = BoneNode
            if (findString BoneNode.name "R Finger0" != undefined) then
                append R_Finger0Arr BoneNode
            if (findString BoneNode.name "R Finger1" != undefined) then
                append R_Finger1Arr BoneNode
            if (findString BoneNode.name "R Finger2" != undefined) then
                append R_Finger2Arr BoneNode
            if (findString BoneNode.name "R Finger3" != undefined) then
                append R_Finger3Arr BoneNode
            if (findString BoneNode.name "R Finger4" != undefined) then
                append R_Finger4Arr BoneNode
        )

        if L_Clavicle != undefined then
            biped.settransform L_Clavicle #rotation ((eulerAngles 0 0 0) as quat) false
        if L_UpperArm != undefined then
            biped.settransform L_UpperArm #rotation ((eulerAngles 0 0 0) as quat) false
        if L_Forearm != undefined then
            biped.settransform L_Forearm #rotation ((eulerAngles 0 0 0) as quat) false
        if L_Hand != undefined then
            biped.settransform L_Hand #rotation ((eulerAngles -90 0 0) as quat) false
        for _bone in L_Finger0Arr do
            biped.settransform _bone #rotation ((eulerAngles -90 0 0) as quat) false
        for _bone in L_Finger1Arr do
            biped.settransform _bone #rotation ((eulerAngles -90 0 0) as quat) false
        for _bone in L_Finger2Arr do
            biped.settransform _bone #rotation ((eulerAngles -90 0 0) as quat) false
        for _bone in L_Finger3Arr do
            biped.settransform _bone #rotation ((eulerAngles -90 0 0) as quat) false
        for _bone in L_Finger4Arr do
            biped.settransform _bone #rotation ((eulerAngles -90 0 0) as quat) false

        if R_Clavicle != undefined then
            biped.settransform R_Clavicle #rotation ((eulerAngles 0 180 0) as quat) false
        if R_UpperArm != undefined then
            biped.settransform R_UpperArm #rotation ((eulerAngles 0 180 0) as quat) false
        if R_Forearm != undefined then
            biped.settransform R_Forearm #rotation ((eulerAngles 0 180 0) as quat) false
        if R_Hand != undefined then
            biped.settransform R_Hand #rotation ((eulerAngles 90 180 0) as quat) false
        for _bone in R_Finger0Arr do
            biped.settransform _bone #rotation ((eulerAngles 90 180 0) as quat) false
        for _bone in R_Finger1Arr do
            biped.settransform _bone #rotation ((eulerAngles 90 180 0) as quat) false
        for _bone in R_Finger2Arr do
            biped.settransform _bone #rotation ((eulerAngles 90 180 0) as quat) false
        for _bone in R_Finger3Arr do
            biped.settransform _bone #rotation ((eulerAngles 90 180 0) as quat) false
        for _bone in R_Finger4Arr do
            biped.settransform _bone #rotation ((eulerAngles 90 180 0) as quat) false

        clearSelection()
        if chkBipedOnly.checked then
        (
            local allBiped = GetAllBipedObjects()
            selectmore allBiped
        )
        else
        (
            SelectSkeletonAll()
        )

        filename = substring filename 1 (filename.count-4)
        filename = filename + textFBXSuffix.text + ".fbx"
        local dir = getFilenamePath filename
        if not isDirectoryWriteable dir then makeDir dir all:true

        exportFile filename #noPrompt selectedOnly:true using:FBXEXP

        max fetch
        setquietmode false

        messagebox("成功导出Tpose FBX骨架！     ")
        return true
    )

	on btnatchExecute pressed do
	(
		try
		(
			--maxfilepath 	--当前打开的文件的文件路径
			--maxfilename  	--当前打开的文件名
			RigFilePath = maxfilepath + maxfilename							--获取当前打开文件的绝对路径
			local fbxfilearray = getFilesRecursive MocapFbxPath.text "*.fbx"		--获取文件夹内所有fbx
			local filecount = fbxfilearray.count							--统计fbx数量
			pluginManager.loadClass FbxImporter
			for i = 1 to filecount do 
			(
				local exportFilePath = substituteString fbxfilearray[i] ".fbx" ".max"
				FBXImporterSetParam "Mode" #merge
				FBXImporterSetParam "Animation" true
				FBXImporterSetParam "FillTimeline" true
				FBXImporterSetParam "BakeAnimationLayers" true
				FBXImporterSetParam "PointCache" true
				loadMaxFile RigFilePath useFileUnits:true quiet:true #noPrompt    --打开rig文件
				frameRate = 30
				importFile fbxfilearray[i] #noPrompt
				frameRate = 60
				saveMaxFile exportFilePath
				resetMaxFile #noprompt		--重置maxfile
			)
			messagebox("全部转换完成！      ")
		)
		catch();
	)
	
	--获取motion data 目录
	on btnSelectMocapFbxPath pressed do
	(
		global tp_MaxfileArray=#()
		tp_Batch = getSavePath caption:"请选择动捕数据源文件目录"
        if tp_Batch == undefined then
        (
            return false
        )
		MocapFbxPath.text = tp_Batch 
		tp_MaxfileArray = getfiles (tp_Batch+"\*.fbx" as string)
		tp_Direcories = getDirectories (tp_Batch+"\*" as string)
		for i in tp_Direcories do
		(
			tp_MaxfileArrayFoo = getfiles (i+"*.fbx" as string)
			for a in tp_MaxfileArrayFoo do append tp_MaxfileArray a
		)
	)
)

rollout Common_MocapBipedTool "动捕相关工具"
(
	button btnOpenDialogPanel1 "动捕相关工具" toolTip:"打开动捕相关工具面板" align:#center width:100
	on btnOpenDialogPanel1 pressed do
	(
		if MocapBipedToolsRollout != undefined then
		(
			DestroyDialog MocapBipedToolsRollout
		)
		createDialog MocapBipedToolsRollout modal:false
	)
)

rollout Common_BatchImportExportAnim "动画批量导入导出"
(
    label lblSrcText "源文件夹路径 (如D:\\AnimOld)"
	editText textSrcPath "" align:#left width:140 tooltip:"填写源文件夹路径名，例如D:\\AnimOld"
    label lblDstText "输出文件夹路径 (如D:\\AnimNew)"
	editText textDstPath "" align:#left width:140 tooltip:"填写导出文件夹路径名，例如D:\\AnimNew"
    label lblRefText1 "输出文件夹下 蒙皮max文件名"
    label lblRefText2 "(如NewSkin.max)"
	editText textRefPath "" align:#left width:140 tooltip:"填写导出文件夹下的max文件名，例如NewSkin.max"

    checkbox chkOutputBone "导出Bone" align:#left checked:true across:2
    checkbox chkMultiBiped "多套CS" align:#right checked:false enabled:false tooltip:"多套CS骨骼，按照Root点匹配名字，分别导出.bip文件并导入"
	button btnGenerate "批量执行"
    button btnBatchLoadCustomBoneData "批量自定义数据"
    button btnSingleLoadCustomBoneData "单个自定义数据"
	
	local srcPath = ""
	local dstPath = ""
	local refPath = ""	
    local refMaxFilePath = ""
    local exportBone = true
    local multiBiped = false
	
	on textSrcPath entered text do
	(
		srcPath = textSrcPath.text
	)

	on textDstPath entered text do
	(
		dstPath = textDstPath.text
	)
	
	on textRefPath entered text do
	(
		refPath = textRefPath.text
	)

    on chkOutputBone changed theState do
    (
        exportBone = theState
    )

    on chkMultiBiped changed theState do
    (
        multiBiped = theState
    )

    fn getAllBipedObjects = 
    (
        local bipedObjects = #()
        for geo in geometry where (isKindOf geo Biped_Object) do
        (
	        append bipedObjects geo
        )
        return bipedObjects
    )

    fn getAllBoneGeometry = 
    (
        local boneObjects = #()
        for geo in geometry where (isKindOf geo BoneGeometry) do
        (
	        append boneObjects geo
        )
        return boneObjects
    )

    fn getAllHelpers = 
    (
        local helperArray = #()
        for _node in helpers where (classOf _node != Particle_View) do
        (
            append helperArray _node
        )
        return helperArray
    )

    fn getAllShapes = 
    (
        return shapes
    )

    fn exportAnimTxtFile fileName = 
    (
        local rangeOutFile = createfile (fileName + "_range.txt")
        local bipedOutFile = createfile (fileName + "_biped.txt")
        local boneOutFile = createfile (fileName + "_bone.txt")
        local boneLinkedOutFile = createfile (fileName + "_bone_linked.txt")

        local bipedObjs = getAllBipedObjects()
        local boneObjs = #()
		local boneObjects = getAllBoneGeometry()
		local helperObjects = getAllHelpers()
		local shapeObjects = getAllShapes()
		join boneObjs boneObjects
		join boneObjs helperObjects
		join boneObjs shapeObjects

        local animLayerBones = #()

        format "%\n" animationRange.start to:rangeOutFile
        format "%\n" animationRange.end to:rangeOutFile
	    close rangeOutFile

        -- bipedKeyPerFrame
        local bipedKeyPerFrameObjects = #()
		-- filter biped per frame objects (by checking the node property "ik space" is set to "object")
        for obj in bipedObjs do
        (
            for i = 1 to obj.controller.keys.count do
            (
                if (biped.getKey obj.controller i).type == #body and (biped.getKey obj.controller i).ikSpace == 1 then
                (
                    append bipedKeyPerFrameObjects obj
                    exit
                )
            )
        )

        for i = animationRange.start to animationRange.end do
        (
            format "#%\n" i to:bipedOutFile
            at time i
            (
                for obj in bipedKeyPerFrameObjects do
                (
                    local _nodeName = obj.name
                    local _position = Biped.getTransform obj #pos
                    local _rotation = Biped.getTransform obj #rotation
                    local _scale = Biped.getTransform obj #scale
                    local str = "@" + _nodeName + "@" + (_position as string) + "@" + (_rotation as string) + "@" + (_scale as string) + "\n"
                    format "%" str to:bipedOutFile
                )
            )
        )
        close bipedOutFile

		if exportBone then
		(
            /*
            -- Gather all nodes that have anim layers
            for obj in boneObjs do
            (
                if (classOf (obj.controller) == prs) then
                (
                    if classOf(obj.position.controller) == Position_Layer or classOf(obj.rotation.controller) == Rotation_Layer or classOf(obj.scale.controller) == Scale_Layer then
                    (
                        append animLayerBones obj
                    )
                )
            )
            
            -- Collapse all bone nodes that have anim layers
            local animLayerIndexArray = AnimLayerManager.getNodesLayers animLayerBones
            sort animLayerIndexArray

            if animLayerIndexArray.count > 0 then
            (
                for i = animLayerIndexArray.count to 1 by -1 do
                (
                    local _layerIndex = animLayerIndexArray[i]
                    if not (_layerIndex == 1 or AnimLayerManager.getLayerName _layerIndex == "Base Layer" or AnimLayerManager.getLayerName _layerIndex == "基础层") then
                    (
                        AnimLayerManager.collapseLayerNodes _layerIndex animLayerBones
                    )
                )
            )
            
            -- Disable the base anim layer
            for _curAnimLayerBone in animLayerBones do
            (
                AnimLayerManager.disableLayerNodes _curAnimLayerBone                         
            )
            */

            -- All Bone Data
			for obj in boneObjs do
			(
				-- Max.chm Link_Constraint - superclass: Matrix3Controller
				if (classOf (obj.controller) == Link_Constraint) then
				(
                    local str = "#" + obj.name + "\n"
                    format "%" str to:boneLinkedOutFile
                    
                    local numLinkTargets = obj.controller.getNumTargets()
                    for i = 1 to numLinkTargets do
                    (
                        local _linkto = obj.controller.getNode i
                        local _linkname = "#LinkToWorld#"
                        if _linkto != undefined then
                        (
                            _linkname = _linkto.name
                        )
                        local _keymode = obj.controller.key_mode
                        local _linkframe = obj.controller.getFrameNo i
                        local _position = (obj.controller.Link_Params.position)
                        local _rotation = (obj.controller.Link_Params.rotation)
                        local _scale = (obj.controller.Link_Params.scale)

                        local str = "@" + "Link_Constraint" + "@" + (_keymode as string) + "@" + (_linkname as string) + "@" + (_linkframe as string) + "@" + (_position as string) + "@" + (_rotation as string) + "@" + (_scale as string) + "\n"
                        format "%" str to:boneLinkedOutFile
                    )

                    local scaleKeys = obj.transform.controller.link_params.position.controller.keys
                    local rotKeys = obj.transform.controller.link_params.position.controller.keys
                    local posKeys = obj.transform.controller.link_params.position.controller.keys

                    -- Link_Constraint_Scale
                    if scaleKeys.count > 0 then
                    (
                        for k in scaleKeys do
                        (
                            at time k.time
                            (
                                local str = "@" + "Link_Constraint_Scale" + "@" + (k.time as string) + "@" + (obj.transform.controller.link_params.scale as string) + "\n"
                                format "%" str to:boneLinkedOutFile
                            )
                        )
                    )
                    else
                    (
                        at time animationRange.start
                        (
                            local str = "@" + "Link_Constraint_Scale" + "@" + (animationRange.start as string) + "@" + (obj.transform.controller.link_params.scale as string) + "\n"
                            format "%" str to:boneLinkedOutFile
                        )
                    )

                    -- Link_Constraint_Rotation
                    if rotKeys.count > 0 then
                    (
                        for k in rotKeys do
                        (
                            at time k.time
                            (
                                local str = "@" + "Link_Constraint_Rotation" + "@" + (k.time as string) + "@" + (obj.transform.controller.link_params.rotation as string) + "\n"
                                format "%" str to:boneLinkedOutFile
                            )
                        )
                    )
                    else
                    (
                        at time animationRange.start
                        (
                            local str = "@" + "Link_Constraint_Rotation" + "@" + (animationRange.start as string) + "@" + (obj.transform.controller.link_params.rotation as string) + "\n"
                            format "%" str to:boneLinkedOutFile
                        )
                    )

                    -- Link_Constraint_Translation
                    if posKeys.count > 0 then
                    (
                        for k in posKeys do
                        (
                            at time k.time
                            (
                                local str = "@" + "Link_Constraint_Translation" + "@" + (k.time as string) + "@" + (obj.transform.controller.link_params.position as string) + "\n"
                                format "%" str to:boneLinkedOutFile
                            )
                        )
                    )
                    else
                    (
                        at time animationRange.start
                        (
                            local str = "@" + "Link_Constraint_Translation" + "@" + (animationRange.start as string) + "@" + (obj.transform.controller.link_params.position as string) + "\n"
                            format "%" str to:boneLinkedOutFile
                        )
                    )
				)
				else if (classOf (obj.controller) == prs) then
				(
                    local str = "#" + obj.name + "\n"

                    -- 在上面塌陷+屏蔽动画层之后不应该再有AnimLayer
                    local haveAnimLayer = false
                    if classOf(obj.position.controller) == Position_Layer or classOf(obj.rotation.controller) == Rotation_Layer or classOf(obj.scale.controller) == Scale_Layer then
                    (
                        haveAnimLayer = true
                        print("### Node Have AnimLayer：" + obj.name)
                    )
                    
                    if haveAnimLayer then
                    (
                        for _frametime = animationRange.start to animationRange.end do
                        (
                            at time _frametime
                            (
                                local selfMat = obj.transform
                                local _position = selfMat.translationpart
                                local _rotation = selfMat.rotationpart
                                local _scale = selfMat.scalepart
                                str = str + "@" + "prs" + "@" + (_frametime as string) + "@" + (_position as string) + "@" + (_rotation as string) + "@" + (_scale as string) + "\n"
                            )
                        )
                    )
                    else
                    (
                        local keyPosition = obj.position.controller.keys
                        local keyRotation = obj.rotation.controller.keys
                        local keyScale = obj.scale.controller.keys
                        local keys = #()
    
                        for k in keyPosition do
                        (
                            appendIfUnique keys k.time
                        )
                        for k in keyRotation do
                        (
                            appendIfUnique keys k.time
                        )
                        for k in keyScale do
                        (
                            appendIfUnique keys k.time
                        )
    
                        if keys.count > 0 then
                        (
                            for key in keys do
                            (
                                local _frametime = key
                                at time _frametime
                                (
                                    local selfMat = obj.transform
                                    local _position = selfMat.translationpart
                                    local _rotation = selfMat.rotationpart
                                    local _scale = selfMat.scalepart
                                    str = str + "@" + "prs" + "@" + (_frametime as string) + "@" + (_position as string) + "@" + (_rotation as string) + "@" + (_scale as string) + "\n"
                                )
                            )
                        )
                        else
                        (
                            local _frametime = animationRange.start
                            at time _frametime
                            (
                                local selfMat = obj.transform
                                local _position = selfMat.translationpart
                                local _rotation = selfMat.rotationpart
                                local _scale = selfMat.scalepart
                                str = str + "@" + "prs" + "@" + (_frametime as string) + "@" + (_position as string) + "@" + (_rotation as string) + "@" + (_scale as string) + "\n"
                            )
                        )
                    )
                    format "%" str to:boneOutFile
				)
			)
		)
    )

    fn importAnimTxtFile fileName = 
    (
        local rangeOutFileName = (fileName + "_range.txt")
        local bipedPerFrameOutFileName = (fileName + "_biped.txt")
        local boneOutFileName = (fileName + "_bone.txt")
        local boneLinkedOutFileName = (fileName + "_bone_linked.txt")

        local rangeOutTextArray = #()
        local bipedOutFileArray = #()
        local boneOutFileArray = #()
        local boneLinkedOutFileArray = #()

        local rangeOutFile = (openFile rangeOutFileName mode:"r")
        if rangeOutFile != undefined then
        (
            while not eof rangeOutFile do
            (
                append rangeOutTextArray (readline rangeOutFile)
            )
            close rangeOutFile
                
            animationRange = interval (execute rangeOutTextArray[1]) (execute rangeOutTextArray[2])
            slidertime = animationRange.start
        )

        local bipedOutFile = (openFile bipedPerFrameOutFileName mode:"r")
        if bipedOutFile != undefined then
        (
            while not eof bipedOutFile do
            (
                append bipedOutFileArray (readline bipedOutFile)
            )
            close bipedOutFile
        )

        local boneOutFile = (openFile boneOutFileName mode:"r")
        if boneOutFile != undefined then
        (
            while not eof boneOutFile do
            (
                append boneOutFileArray (readline boneOutFile)
            )
            close boneOutFile
        )

        local boneLinkedOutFile = (openFile boneLinkedOutFileName mode:"r")
        if boneLinkedOutFile != undefined then
        (
            while not eof boneLinkedOutFile do
            (
                append boneLinkedOutFileArray (readline boneLinkedOutFile)
            )
            close boneLinkedOutFile
        )

        with animate on
        (
            local curNode = undefined
            local curNodeKeyTimes = #()

            local curFrameTime = undefined
            
            for txt in bipedOutFileArray do
            (
                if txt[1] == "#" then
                (
                    local _frametime = execute (substring txt 2 txt.count)
                    curFrameTime = _frametime
                )
                else if txt[1] == "@" then
                (
                    local transformDataInfo = filterString txt "@"
                    if transformDataInfo.count == 4 then
                    (
                        local _nodeName = transformDataInfo[1]
                        local _position = execute transformDataInfo[2]
                        local _rotation = execute transformDataInfo[3]
                        local _scale = execute transformDataInfo[4]
                        curNode = getNodeByName _nodeName
                        if curNode != undefined then
                        (
                            at time curFrameTime
                            (
                                Biped.setTransform curNode #pos _position true
                                Biped.setTransform curNode #rotation _rotation true
                                Biped.setTransform curNode #scale _scale true
                            )
                        )
                    )
                )
            )

			if exportBone then
            (
				for txt in boneLinkedOutFileArray do
				(
					-- 新的Node
					if txt[1] == "#" then
					(
						local nodeName = substring txt 2 txt.count
						curNode = getNodeByName nodeName
                        if (isKindOf curNode Biped_Object) then
                        (
                            curNode = undefined
                        )
					)
					-- 更新Node每个关键帧时间
					else if txt[1] == "@" then
					(
						local Info = filterString txt "@"
                        if (Info[1] == "Link_Constraint") then
                        (
                            local _keyMode = execute Info[2]
                            local _linktoName = Info[3]
                            local _linkframe = execute Info[4]
                            local _position = execute Info[5]
                            local _rotation = execute Info[6]
                            local _scale = execute Info[7]

                            print("### Link_Constraint" + curNode.name + "   keymode" + (_keyMode as string))

                            if not (classOf (curNode.controller) == Link_Constraint) then
                            (
                                curNode.Controller = Link_Constraint()
                            )
                            curNode.controller.key_mode = _keyMode

                            if _linktoName == "#LinkToWorld#" then
                            (
                                curNode.controller.addWorld frameNo:_linkframe
                            )
                            else
                            (
                                local _linktoObj = getNodeByName _linktoName
                                if _linktoObj != undefined then
                                (
                                    curNode.controller.addTarget _linktoObj _linkframe
                                )
                            )

                            -- print(_linktoName)
                            -- print(_linkframe)
                            -- print(_position)
                            -- print(_rotation)
                            -- print(_scale)

                            at time _linkframe
                            (
                                curNode.controller.Link_Params.Scale = _scale
                                curNode.controller.Link_Params.Rotation = _rotation
                                curNode.controller.Link_Params.Position = _position
                            )
                        )
                        else
                        (
                            if Info[1] == "Link_Constraint_Scale" then
                            (
                                local _frametime = execute Info[2]
                                local _scale = execute Info[3]
                                at time _frametime
                                (
                                    curNode.transform.controller.link_params.scale = _scale
                                    addNewKey curNode.transform.controller.link_params.scale.controller _frametime
                                )
                            )
                            else if Info[1] == "Link_Constraint_Rotation" then
                            (
                                local _frametime = execute Info[2]
                                local _rotation = execute Info[3]
                                at time _frametime
                                (
                                    curNode.transform.controller.link_params.rotation = (inverse _rotation)
                                    addNewKey curNode.transform.controller.link_params.rotation.controller _frametime
                                )
                            )
                            else if Info[1] == "Link_Constraint_Translation" then
                            (
                                local _frametime = execute Info[2]
                                local _position = execute Info[3]
                                at time _frametime
                                (
                                    curNode.transform.controller.link_params.position = _position
                                    addNewKey curNode.transform.controller.link_params.position.controller _frametime
                                )
                            )
                        )
					)
				)
                
                /*
                for txt in boneOutFileArray do
                (
                    -- 新的Node
                    if txt[1] == "#" then
                    (
                        local nodeName = substring txt 2 txt.count
                        curNode = getNodeByName nodeName
                        if (isKindOf curNode Biped_Object) then
                        (
                            curNode = undefined
                        )
                    )
                    -- 更新Node每个关键帧时间
                    else if txt[1] == "@" then
                    (
                        if curNode != undefined then
                        (
                            local Info = filterString txt "@"
                            if Info[1] == "PRS_Scale" then
                            (
                                local _frametime = execute Info[2]
                                local _scale = execute Info[3]
                                at time _frametime
                                (
                                    -- (in coordsys world curNode.scale = _scale)
                                    curNode.scale = _scale
                                    addNewKey curNode.scale.controller _frametime
                                )
                            )
                            else if Info[1] == "PRS_Rotation" then
                            (
                                local _frametime = execute Info[2]
                                local _rotation = execute Info[3]
                                at time _frametime
                                (
                                    -- (in coordsys world curNode.rotation = inverse _rotation)
                                    curNode.rotation = inverse _rotation
                                    addNewKey curNode.rotation.controller _frametime
                                )
                            )
                            else if Info[1] == "PRS_Translation" then
                            (
                                local _frametime = execute Info[2]
                                local _position = execute Info[3]
                                at time _frametime
                                (
                                    -- (in coordsys world curNode.position = _position)
                                    curNode.position = _position
                                    addNewKey curNode.position.controller _frametime
                                )
                            )
                        )
                    )
                )
                */

                for txt in boneOutFileArray do
                (
                    -- 新的Node
                    if txt[1] == "#" then
                    (
                        local nodeName = substring txt 2 txt.count
                        curNode = getNodeByName nodeName
                        if (isKindOf curNode Biped_Object) then
                        (
                            curNode = undefined
                        )
                    )
                    -- 更新Node每个关键帧时间
                    else if txt[1] == "@" then
                    (
                        if curNode != undefined then
                        (
                            local Info = filterString txt "@"

                            if (Info[1] == "prs" and Info.count == 5) then
                            (
                                if not (classOf (curNode.controller) == prs) then
                                (
                                    curNode.Controller = prs()
                                )

                                local _frametime = execute Info[2]
                                local _position = execute Info[3]
                                local _rotation = execute Info[4]
                                local _scale = execute Info[5]

                                at time _frametime
                                (
                                    (in coordsys world curNode.scale = _scale)
                                    (in coordsys world curNode.rotation = inverse _rotation)
                                    (in coordsys world curNode.position = _position)
                                )
                            )
                        )
                    )
                )
			)
        )
    )

    fn exportBip = 
    (
        /*
        local dir_array = GetDirectories (srcPath+"*")
        for d in dir_array do
            exportBip d
        */

        local ExportFailedMaxFileNames = ""

        for f in getfiles (srcPath + "\\*.max") do 
        (
            print f
            loadMaxFile f usefileunits:true
            local fileName = getFilenameFile maxFileName

            -- 隐藏所有非bone的Geometry
            hideByCategory.none() 
            hideByCategory.geometry = true
            max unhide all #noPrompt
            for i = 1 to GetNumberDisplayFilters() do
                SetDisplayFilter i false

            max select all

            local allBipObjects = getAllBipedObjects()
            local bipRoots = #()
            
            -- [Vertical_Horizontal_Turn] : Matrix3Controller
            -- This class represents the transform controller for the root object within the biped system. 
            for obj in allBipObjects do
            (
                if classof(obj.controller) == Vertical_Horizontal_Turn then
                    append bipRoots obj
            )
            
            if multiBiped == false and bipRoots.count > 1 then
            (
                messagebox ("导出失败！有多套CS骨骼，且未勾选【多套CS】选项")
                print ("导出失败！有多套CS骨骼，且未勾选【多套CS】选项 " + maxFileName)
				continue
            )

            if bipRoots.count > 0 then
            (
                if multiBiped == false then
                (
                    local bipFilePath = dstPath + "\\AnimFiles\\" + fileName + ".bip"
                    local controller = bipRoots[1].controller
                    biped.saveBipFileSegment controller bipFilePath animationRange.start animationRange.end
                )
                else
                (
                    for obj in bipRoots do
                    (
                        local bipFilePath = dstPath + "\\AnimFiles\\" + fileName + obj.name + ".bip"
                        local controller = obj.controller
                        biped.saveBipFileSegment controller bipFilePath animationRange.start animationRange.end
                    )
                )
            )

            if exportBone and bipRoots.count > 0 then
            (
                local xafFilePath = dstPath + "\\AnimFiles\\" + fileName + ".xaf"
                
                clearSelection()
                local boneObjects = getAllBoneGeometry()
                local helperObjects = getAllHelpers()
                local shapeObjects = getAllShapes()
                selectmore boneObjects
                selectmore helperObjects
                selectmore shapeObjects
                local success = LoadSaveAnimation.saveAnimation xafFilePath $ #() #()
                if success == false then
                (
                    print("Save Animation Failed For File " + maxFileName)
                    ExportFailedMaxFileNames = ExportFailedMaxFileNames + (maxFileName + "\n")
                )
            )
            
            -- Export Custom Data
            local txtFilePath = dstPath + "\\AnimFiles\\" + fileName
            exportAnimTxtFile txtFilePath

            -- copy maxfile to dstPath
			refMaxFilePath = dstPath + "\\" + refPath
			generatedMaxFilePath = dstPath + "\\" + fileName + ".max"
			copyFile refMaxFilePath generatedMaxFilePath
        )

        if ExportFailedMaxFileNames == "" then
        (
            messageBox("动画导出成功")
        )
        else
        (
            messageBox("以下Bone动画导出失败:           \n" + ExportFailedMaxFileNames)
        )
    )

    fn importBip = 
    (
        for f in getfiles (dstPath + "\\*.max") where f != refMaxFilePath do 
        (
            print("******************** Import Anim " + f)
            loadMaxFile f usefileunits:true
            local fileName = getFilenameFile maxFileName

            --删除所有关键帧 TODO
            max select all
            macros.run "Animation Tools" "DeleteSelectedAnimation"
            clearSelection()
            
            --隐藏所有非bone的Geometry
            hideByCategory.none() 
            hideByCategory.geometry = true
            
            setquietmode true
            max unhide all #noPrompt

            for i = 1 to GetNumberDisplayFilters() do
                SetDisplayFilter i false
            
            local allBipObjects = getAllBipedObjects()
            local bipRoots = #()
            if multiBiped == false and bipRoots.count > 1 then
            (
                messagebox ("导出失败！有多套CS骨骼，请勾选【多套CS】选项")
                return false
            )

            -- [Vertical_Horizontal_Turn] : Matrix3Controller
            -- This class represents the transform controller for the root object within the biped system. 
            local bipPosList_WorldSpace = #()
			local bipRotList_WorldSpace = #()

            for o in allBipObjects do
            (
                if classof(o.controller) == Vertical_Horizontal_Turn then
                    append bipRoots o
            )

            if bipRoots.count > 0 then
            (
                if multiBiped == false then
                (
                    local obj = bipRoots[1]
                    local bipFilePath = dstPath + "\\AnimFiles\\" + fileName + ".bip"
                    local controller = obj.controller
                    biped.loadBipFile controller bipFilePath
                )
                else
                (
                    for obj in bipRoots do
                    (
                        local bipFilePath = dstPath + "\\AnimFiles\\" + fileName + obj.name + ".bip"
                        local controller = obj.controller
                        biped.loadBipFile controller bipFilePath
                    )
                )
            )
            
            if exportBone and bipRoots.count > 0 then
            (
                local xafFilePath = dstPath + "\\AnimFiles\\" + fileName + ".xaf"
                
                -- 防止有些bone选不中
                local boneObjects = getAllBoneGeometry()
                local helperObjects = getAllHelpers()
                local shapeObjects = getAllShapes()
                clearSelection()
                selectmore boneObjects
                selectmore helperObjects
                selectmore shapeObjects
                local success = LoadSaveAnimation.loadAnimation xafFilePath $ relative:false insert:false
                if success == false then
                (
                    print("Load Animation Failed For File " + maxFileName)
                )
            )
            
            saveMaxFile f
        )
    )
    
    fn importCustomData = 
    (
        for f in getfiles (dstPath + "\\*.max") where f != refMaxFilePath do 
        (
            print("******************** Import Custom Anim " + f)
    
            loadMaxFile f usefileunits:true
            local fileName = getFilenameFile maxFileName
    
            local txtFilePath = dstPath + "\\AnimFiles\\" + fileName
            importAnimTxtFile txtFilePath
            
            saveMaxFile f
        )
    )
    
    on btnGenerate pressed do
	(
		if srcPath == "" then
			messagebox("原始路径未设置")
		else if dstPath == "" then
			messagebox("目标路径未设置")
		else if refPath == "" then
			messagebox("参考文件未设置")
        else if not isDirectoryWriteable dstPath then
            messagebox("导出路径不存在")
		else (
			local refFile = dstPath + "\\" + refPath
			if (not (doesFileExist refFile)) then
			(
				messagebox("参考文件不存在")
				return 0					
			)

			setquietmode true
            -- disableRefMsgs() 
            
			tmpPath = dstPath + "\\AnimFiles"
			makeDir tmpPath
			exportBip()
            
			importBip()
            importCustomData()

			setquietmode false
            -- enableRefMsgs()
		)
	)

    on btnBatchLoadCustomBoneData pressed do
    (
        importCustomData()
    )

    on btnSingleLoadCustomBoneData pressed do
    (
        local fileName = getFilenameFile maxFileName
        local txtFilePath = dstPath + "\\AnimFiles\\" + fileName
        importAnimTxtFile txtFilePath
    )
)

rollout Common_DebugTool "Debug工具"
(
	button btnSelectMoreBiped "加选CS骨骼"
	button btnSelectMoreBone "加选Bone骨骼"
	button btnSelectMoreMesh "加选模型"
	button btnPrintAllSelectedTransform "print选中骨骼Transform"
	button btnPrintAllBoneTransform "print所有骨骼Transform"

    button btnTest1 "Debug 1"
    button btnTest2 "Debug 2"
    button btnTest3 "Debug 3"
    button btnTest4 "Debug 4"
    button btnTest5 "Debug 5"

	on btnSelectMoreBiped pressed do
	(
		local a = A2UFn_GetAllBipedObject()
		selectmore a
	)
	
	on btnSelectMoreBone pressed do
	(
		local a = A2UFn_GetAllBoneObject()
		local b = A2UFn_GetAllShape()
		local c = A2UFn_GetAllHelper()
		selectmore a
		selectmore b
		selectmore c
	)

	on btnSelectMoreMesh pressed do
	(
		local a = A2UFn_GetAllMesh()
		selectmore a
	)

	on btnPrintAllSelectedTransform pressed do
	(
		local selectedNodes = getCurrentSelection()
		local _start = animationrange.start
		local _end = animationrange.end
		for t = _start to _end do
		(
			A2UFn_PrintNodesTransformAtFrame selectedNodes t
		)
	)

	on btnPrintAllBoneTransform pressed do
	(
		local _start = animationrange.start
		local _end = animationrange.end
	)

	on btnTest1 pressed do
	(
        A2UFn_SelectAllSkeleton($)
	)

	on btnTest2 pressed do
	(
	)

	on btnTest3 pressed do
	(
	)

	on btnTest4 pressed do
	(
	)

	on btnTest5 pressed do
	(
	)

)